package ltd.nullpointer.tcp.core.serialize;

import com.boot2.core.utils.ByteUtils;
import com.boot2.core.utils.ReflectUtil;
import com.boot2.core.utils.StringUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import ltd.nullpointer.tcp.core.annotation.TcpRequired;
import ltd.nullpointer.tcp.core.annotation.TcpUpOffset;
import ltd.nullpointer.tcp.core.constant.TCPEnum;
import ltd.nullpointer.tcp.core.exception.MessageSerializerException;
import ltd.nullpointer.tcp.core.message.AbstractTCPMessage;
import ltd.nullpointer.tcp.core.message.NPiotTCPMessage;
import ltd.nullpointer.tcp.core.resolver.AbstractTCPMessageClassFieldEntry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.List;

/**
 * 平台业务消息序列化器
 *
 * @author zhangweilin
 * @ClassName: NPiotTCPMessageSerializer.java
 * @Description:
 * @date 2018年1月30日 下午3:22:01
 */
public abstract class NPiotTCPMessageSerializer extends AbstractTCPMessageClassFieldEntry implements MessageSerializer {
    protected Log log = LogFactory.getLog(this.getClass());

    @Override
    public ByteBuf serialize(AbstractTCPMessage abstractTCPMessage) {
        NPiotTCPMessage npiotTCPMessage = null;
        if (abstractTCPMessage instanceof NPiotTCPMessage) {
            npiotTCPMessage = (NPiotTCPMessage) abstractTCPMessage;
        } else {
            throw new IllegalArgumentException("abstractTCPMessage类型必须为 " + NPiotTCPMessage.class);
        }
        System.out.println("NPiotTCPMessageSerializer.abstractTCPMessage: " + abstractTCPMessage);
        StringBuffer sb = new StringBuffer();

        String msgCode = npiotTCPMessage.getMsgCode();
        Integer msgSn = npiotTCPMessage.getMsgSn();
        String deviceId = npiotTCPMessage.getDeviceSn();
        // String payload = npiotTCPMessage.getPayload();
        // TODO 这里应该要从库中读取协议,协议条目的数据类型字段里，要增加一个长度类型，为某一个字段计算长度，

        // TODO 如果发送时，payload 是一个内置协议的组件，则根据msgCode找到对应实体，根据上面标的长度，序列化成一个字符串

        sb.append(StringUtils.leftPad(msgCode + "", 3, "0"));
        sb.append(StringUtils.leftPad(msgSn + "", 2, "0"));
        sb.append(StringUtils.leftPad(deviceId.length() + "", 2, "0"));
        sb.append(deviceId);
        sb.append("2");

        // 如果已经计算好了字节数组，则无须重复序列化
        byte[] byteArr = npiotTCPMessage.getPayloadByte();

        // 字符串拼接后转字节
        if (null == byteArr || byteArr.length == 0) {
            String payload2 = serializeBean(npiotTCPMessage);
            sb.append(payload2);
            String payload3 = sb.toString();
            byteArr = ByteUtils.getBytes(payload3);
        } else {
            // 两个字节数组拼接
            // 这种情况，一般是由云端或app下发mqtt消息时，mqtt要转成tcp消息下发到设备
            byte[] byteHeader0 = ByteUtils.getBytes(sb.toString());
            ByteBuffer messageByteBuffer0 = ByteBuffer.allocate(byteHeader0.length + byteArr.length);
            messageByteBuffer0.put(byteHeader0);// 此时为内容里的头，比如msgCode,MsgSn,deviceId等
            messageByteBuffer0.put(byteArr); // 此时为数据字节
            byteArr = messageByteBuffer0.array();
        }
        int capacity = abstractTCPMessage.getHeader().length + byteArr.length + abstractTCPMessage.getTail().length;
//        ByteBuffer messageByteBuffer = ByteBuffer.allocate(capacity);
//        messageByteBuffer.put(abstractTCPMessage.getHeader());
//        messageByteBuffer.put(byteArr);
//        messageByteBuffer.put(abstractTCPMessage.getTail());
//        System.out.println("最终发送字节: " + CHexConver.byte2HexStr(messageByteBuffer.array()));
        ByteBuf byteBuf = PooledByteBufAllocator.DEFAULT.directBuffer(capacity);
        byteBuf.writeBytes(abstractTCPMessage.getHeader());
        byteBuf.writeBytes(byteArr);
        byteBuf.writeBytes(abstractTCPMessage.getTail());
        return byteBuf;
    }

    private String serializeBean(NPiotTCPMessage npiotTCPMessage) {
        String msgCode = npiotTCPMessage.getMsgCode();
        TCPEnum.MsgCode msgCode2 = TCPEnum.MsgCode.getMsgCode(msgCode);
        String payload = null;
        if (null != msgCode2) {
//            payload = "";
            // 从对象中读取数据和长度，组装成payload
            ClassFieldEntry classFieldEntry = classFieldEntryMap.get(msgCode2.getMessageCode() + "");
            // 有msgCode并且能找到相应类，说明是对该msgCode定义过实体类，这种一般是平台内置的业务功能或者说内置的解析规则，则走实体类解析,否则走用户自定义解析规则
            StringBuffer sb = new StringBuffer();
            if (null != classFieldEntry) {
                Object object = npiotTCPMessage.getPayload();
                List<Field> fieldList = classFieldEntry.getFieldList();
                fieldList.forEach(e -> {
                    try {
//                        Method getMethod = object.getClass().getMethod("get" + StringUtils.changFirstWord(e.getName(), StringUtils.toUpperCase));
//                        Invokers.Invoker get = Invokers.newInvoker(getMethod);
//                        Object getValue = get.invoke(object, null);
                        Object getValue = ReflectUtil.getFieldValue(object, e);
                        System.out.println(e.getName() + " 获取到getValue: " + getValue);
                        String str = "";
                        if (null != getValue) {
                            str = getValue.toString();
                        }
                        TcpRequired tcpRequired = e.getAnnotation(TcpRequired.class);
                        TcpUpOffset tcpOffset = e.getAnnotation(TcpUpOffset.class);
                        System.out.println("tcpRequired: " + tcpRequired);
                        if (StringUtils.isEmpty(str) && !tcpRequired.value()) {
                            log.debug("检测到字段  【" + e.getName() + "】为空，系统标记为非必传，将不生成此字段报文");
                        } else {
                            int length = tcpOffset.end()-tcpOffset.start()+1;
                            // 做长度修正(内置解析规则，无需考虑长度类型)
                            str = StringUtils.leftPadAndSubString(String.valueOf(str), length, "0");
                            sb.append(str);
                        }
                    } catch (SecurityException e1) {
                        e1.printStackTrace();
                        throw new MessageSerializerException(TCPEnum.ErrorCode.err10008.getErrCode(), TCPEnum.ErrorCode.err10008.getName() + ",字段: " + e.getName(), e1);
                    }
                });
                payload = sb.toString();
                System.out.println("解析出内置组件payload: " + payload);
                return payload;
            }
        }
        payload = serialize(npiotTCPMessage);
        return payload;
    }

    public abstract String serialize(NPiotTCPMessage npiotTCPMessage);

}
